import os
import re
import select
import socket
import struct
import threading
import time
import tkinter as tk
import sys

from tkinter import filedialog, messagebox
from tkinter import ttk

import gui_util.gui_common as gui_common

# def open_file(label):
#     filepath = filedialog.askopenfilenames()  # 打开文件对话框并获取选择的文件路径
#     if filepath:  # 如果用户选择了文件
#         label.config(text="选择的文件: " + ",".join(filepath))  # 更新标签显示文件路径

# def add_text():
#     text_box.insert(tk.END, "这是新的一行文本。\n")
#     # 移动滚动条到底部
#     text_box.see(tk.END)

# reading = False
# def read_context(window):
#     global reading
#     if reading:  # 如果已经在读取中，则不执行
#         messagebox.showinfo("提示", "文件正在读取，请稍候...")
#         return
#     reading = True
#     try:
#         with open("data/file.txt", "r") as file:
#             lines = []
#             for line in file:
#                 if stop_event.is_set():
#                     break
#                 lines.append(line.strip('\n'))
#                 if len(lines) > 10:
#                     content = "".join(lines)
#                     time.sleep(1)
#                     if stop_event.is_set():
#                         break
#                     text_box.insert(tk.END, content + "\n")
#                     # 移动滚动条到底部
#                     text_box.see(tk.END)
#                     window.update_idletasks()  # 更新UI，使文本立即显示
#                     lines.clear()
#     finally:
#         # 确保读取结束后重置状态标志
#         reading = False

# def safe_read_and_display(window):
#     # 在新线程中执行文件读取操作，避免阻塞UI
#     read_context_thread = threading.Thread(target= lambda:read_context(window))
#     read_context_thread.start()


stop_event = threading.Event()


# 主窗口关闭时，设置退出标志
def on_closing(window):
    global stop_event
    stop_event.set()
    # 可能还需要调用thread.join()来等待线程结束，但这会阻塞主窗口关闭，需谨慎使用
    window.destroy()  # 假设root是你的Tkinter窗口实例


sock_to_item = {}

recv_text_lock = threading.Lock()


def insert_colored_line(text_widget, text, color="blue"):
    """在Text小部件中插入带有指定颜色的文本行"""
    # 定义文本颜色标签
    text_widget.tag_configure(color, foreground=color)

    # 插入文本并应用颜色标签
    end_index = text_widget.index(tk.END)
    # 确保插入点在末尾，如果有换行符则跳过它
    if text_widget.get(end_index, tk.END).strip() == "":
        text_widget.insert(tk.END, text + "\n", color)
    else:
        text_widget.insert(tk.END, "\n" + text + "\n", color)
    text_widget.see(tk.END)


def create_connection_thread(tree, text_box, sock):
    # 设置接收超时时间为5秒
    timeout = 2  # 超时时间，单位为秒
    sock.setsockopt(
        socket.SOL_SOCKET, socket.SO_RCVTIMEO, struct.pack("ll", timeout * 1000, 0)
    )
    while not stop_event.is_set():
        try:
            item_id, conn_str = sock_to_item[sock]
            data = sock.recv(1024)
            if data:
                global recv_text_lock
                recv_text_lock.acquire()
                # 将接收到的数据添加到文本框中
                insert_colored_line(text_box, f"{conn_str} Received:", "blue")
                insert_colored_line(text_box, data.decode("utf-8"), "red")
                recv_text_lock.release()
            else:
                print(conn_str, " Connection closed")
                conn_str_to_sock[conn_str] = (sock, "disconnected")
                tree.set(item_id, "connStatCol", "disconnected")
                break
        except socket.timeout:
            # print("Timeout occurred")
            pass
        except Exception as e:
            print(f"Error: {e}")
            break


def create_tcp_connection(tree, combox, text_box):
    user_input = combox.get()
    ip_pattern = (
        r"(\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}):(\d+)"  # IP地址和端口的正则表达式
    )

    match = re.match(ip_pattern, user_input)
    if match:
        host = match.group(1)
        port = int(match.group(2))
    else:
        messagebox.showerror(
            "输入错误", "请输入有效的IP地址和端口号，格式如: 127.0.0.1:12345"
        )
        return
    try:
        s = socket.socket(family=socket.AF_INET, type=socket.SOCK_STREAM)
        s.connect((host, port))
        messagebox.showinfo("连接成功", f"已成功连接到{host}:{port}")
        local_info = s.getsockname()  # 获取本地IP和端口
        remote_info = s.getpeername()  # 获取对端IP和端口
        connection_string = (
            f"{local_info[0]}:{local_info[1]} -> {remote_info[0]}:{remote_info[1]}"
        )
        confirm_result = messagebox.askquestion(
            "添加地址", "是否将此连接地址添加到列表？"
        )
        if confirm_result == "yes":
            item_id = tree.insert("", tk.END, values=(connection_string, "connected"))
            global sock_to_item
            sock_to_item[s] = (
                item_id,
                f"{remote_info[0]}:{remote_info[1]} -> {local_info[0]}:{local_info[1]}",
            )
            conn_str_to_sock[connection_string] = (s, "connected")
            threading.Thread(
                target=create_connection_thread, args=(tree, text_box, s)
            ).start()
        else:
            s.close()  # 如果用户取消，关闭连接
    except ConnectionRefusedError:
        messagebox.showerror("连接失败", f"连接{user_input}被拒绝，请检查IP和端口。")


def get_selected_item_col_value(treeview, col_name):
    # 获取当前选中项的ID
    current_selection = treeview.selection()
    if current_selection:
        # 获取列名列表，注意 "#0" 是默认列（ID列）
        columns = list(treeview["columns"])
        for item in current_selection:
            # 获取所有列的值
            item_values = treeview.item(item, "values")
            # 遍历列，打印列名和对应的值)
            for idx, col in enumerate(columns):
                col_value = item_values[idx]
                if col == col_name:
                    return col_value
    return ""


conn_str_to_sock = {}


def send_tcp_data(treeview, text_box, recv_text_box):
    content = text_box.get("1.0", "end-1c")  # "end-1c"用于排除末尾的换行符
    if not content:
        messagebox.showerror("输入错误", "请输入要发送的数据")
        return
    else:
        current_selection = treeview.selection()
        if current_selection:
            conn_str = get_selected_item_col_value(treeview, "addrCol")
            global conn_str_to_sock
            if conn_str in conn_str_to_sock:
                s, status = conn_str_to_sock[conn_str]
                if status == "connected":
                    s.send(content.encode("utf-8"))
                    recv_text_lock.acquire()
                    # 将接收到的数据添加到文本框中
                    insert_colored_line(recv_text_box, f"{conn_str} Sent:", "blue")
                    insert_colored_line(recv_text_box, content, "green")
                    recv_text_lock.release()
                    text_box.delete("1.0", tk.END)
                else:
                    messagebox.showerror("错误", "连接已断开")
        else:
            messagebox.showerror("错误", "请选择一个连接")
            return


def close_tcp_conn(treeview):
    current_selection = treeview.selection()
    if current_selection:
        conn_str = get_selected_item_col_value(treeview, "addrCol")
        global conn_str_to_sock
        if conn_str in conn_str_to_sock:
            s, status = conn_str_to_sock[conn_str]
            if status == "connected":
                s.close()
                conn_str_to_sock[conn_str] = (s, "disconnected")
                treeview.set(current_selection[0], "connStatCol", "disconnected")
            else:
                messagebox.showerror("错误", "连接已断开")
        else:
            messagebox.showerror("错误", "请选择一个连接")
            return


prev_selection = None  # 初始化上一个选中项变量


def on_select(event, treeview):
    try:
        # 获取当前选中项的ID
        current_selection = treeview.selection()
        global prev_selection
        # 检查并处理已删除的前一个选中项
        if prev_selection and not treeview.exists(prev_selection):
            prev_selection = None

        # 如果有之前选中的项，恢复其背景色
        if prev_selection and treeview.exists(prev_selection):
            treeview.item(prev_selection, tags=())

        # 设置当前选中项的标签
        if current_selection:
            treeview.item(current_selection, tags=("selected",))
            # 记录当前选中项以便下次切换
            prev_selection = current_selection[
                0
            ]  # 注意，selection可能返回多个项目，这里取第一个
    except Exception as e:
        print(f"Error during selection: {e}")


def delete_node(treeview):
    """删除选中的节点"""
    status = get_selected_item_col_value(treeview, "connStatCol")
    if status == "connected":
        messagebox.showerror("错误", "请先断开连接")
        return
    if not treeview.selection():
        messagebox.showerror("错误", "请选择一个连接")
        return
    selected_item = treeview.selection()[0]  # 获取选中的项目
    cxt = get_selected_item_col_value(treeview, "addrCol")
    if messagebox.askyesno("确认删除", f"确定要删除节点 '{cxt}' 吗?"):
        global prev_selection
        # 如果即将删除的节点是当前选中的节点，先清除它
        if prev_selection == selected_item:
            prev_selection = None
        treeview.selection_remove(selected_item)  # 移除选中状态
        treeview.delete(selected_item)  # 删除节点


def popup_menu(event, menu):
    """在鼠标右键点击位置显示弹出菜单"""
    try:
        menu.tk_popup(event.x_root, event.y_root)  # 在鼠标位置显示菜单
    finally:
        # 释放鼠标左键，避免菜单保持激活状态
        menu.grab_release()


def on_combobox_focusout(event, combobox, options):
    """处理 Combobox 内容变化的事件"""
    new_value = combobox.get()
    if new_value not in options and new_value:  # 确保新值不为空且不在选项中
        ip_pattern = (
            r"(\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}):(\d+)"  # IP地址和端口的正则表达式
        )
        match = re.match(ip_pattern, new_value)
        if match:
            options.append(new_value)  # 将新值添加到选项列表
            combobox["values"] = options  # 更新Combobox的选项
            print(f"已将 '{new_value}' 添加到选项列表。")
    else:
        print(f"用户选择了或输入了: {new_value}")


def on_treeview_motion(event, tooltip, tree):
    item = tree.identify_row(event.y)
    if item:
        item_values = tree.item(item, "values")
        tooltip.showtip(event, f"{item_values[0]} {item_values[1]}")
    else:
        tooltip.hidetip()


if __name__ == "__main__":
    app = tk.Tk()
    app.title("MockConnect")  # 设置窗口标题
    # gui_util.set_background_image(app, "data/back_ground.jpg")
    # label = tk.Label(app, text="欢迎来到Tkinter世界！")
    # label.pack()  # 使用pack布局管理器添加标签到窗口
    # # 创建一个标签，用于显示用户选择的文件路径
    # label_file_selected = tk.Label(app, text="未选择文件")
    # label_file_selected.pack()
    # button = tk.Button(app, text="点击我", command=lambda: open_file(label_file_selected))
    # button.pack()  # 添加按钮到窗口

    # # 使用grid布局
    # app.columnconfigure(0, weight=1)  # 让第一列填充额外的空间
    # app.rowconfigure(0, weight=1)     # 让第一行填充额外的空间

    # # 创建Text widget并使用grid布局
    # text_box = tk.Text(app, wrap=tk.WORD)
    # text_box.grid(row=0, column=0, sticky=tk.NSEW)  # sticky使得Text在指定方向上扩展

    # # 创建Scrollbar并与Text widget关联
    # scrollbar = tk.Scrollbar(app, orient=tk.VERTICAL, command=text_box.yview)
    # scrollbar.grid(row=0, column=1, sticky=tk.NS)  # 让Scrollbar随窗口垂直方向变化
    # text_box['yscrollcommand'] = scrollbar.set

    # # 添加一些初始文本演示
    # for _ in range(20):
    #     add_text()
    # # 添加一个按钮，点击时向Text widget中添加新文本
    # add_button = tk.Button(app, text="添加文本", command=lambda: safe_read_and_display(app))
    # add_button.grid(row=1, column=0, sticky=tk.EW)  # 让按钮水平填充

    # 创建一个Frame来包裹标题和列表框，便于控制它们的紧凑排列

    frame = tk.Frame(
        app,
        padx=0,
        pady=0,
    )
    frame.grid(
        row=0, column=0, padx=2, pady=(2, 0), sticky=tk.NSEW
    )  # 设置外边距以控制紧凑度

    title_label = tk.Label(
        frame, text="tcp_connections", font=("Arial", 16, "bold"), fg="blue", width=32
    )
    title_label.pack(
        side=tk.TOP,
        fill=tk.BOTH,
    )
    tree_inner_frame = tk.Frame(
        frame,
        padx=0,
        pady=0,
    )
    tree_inner_frame.pack(side=tk.BOTTOM, fill=tk.BOTH, expand=True)

    # 创建一个垂直滚动条
    scrollbar = ttk.Scrollbar(tree_inner_frame)
    scrollbar.pack(side=tk.RIGHT, fill=tk.Y)

    # 定义Treeview和样式
    style = ttk.Style()
    style.configure(
        "Treeview",
        background="white",
        foreground="black",
        rowheight=20,
        fieldbackground="white",
        font=("Helvetica", 10),
    )
    # 自定义标题样式
    style.configure(
        "Treeview.Heading",
        font=("Helvetica", 10, "bold"),  # 设置字体和加粗
        foreground="black",  # 设置标题颜色
        background="lightgray",  # 设置标题背景色，如果需要的话
        relief="solid",
    )  # 设置边框样式

    style.map("Treeview", background=[("selected", "lightblue")])  # 设置选中行的背景色

    tree = ttk.Treeview(
        master=tree_inner_frame,
        columns=("addrCol", "connStatCol"),
        show="headings",
        selectmode="browse",
        yscrollcommand=scrollbar.set,
    )
    tree["displaycolumns"] = ("addrCol", "connStatCol")  # 显示列标题

    tree.heading("addrCol", text="address", anchor=tk.W)  # 设置列标题
    tree.heading("connStatCol", text="connection status", anchor=tk.W)
    tree.tag_configure("selected", background="lightgreen")  # 定义一个tag来改变颜色
    tree.column("addrCol", width=200, stretch=tk.YES)  # 第一列固定宽度
    tree.column("connStatCol", width=50, stretch=tk.YES)  # 第一列固定宽度

    tree.pack(side=tk.LEFT, fill=tk.BOTH, expand=True)

    tree.bind(
        "<<TreeviewSelect>>", lambda event: on_select(event, tree)
    )  # 绑定选择事件
    # # 创建一个Listbox，设定宽度为30单位（这里以像素为例）
    # listbox = tk.Listbox(frame, width=40,yscrollcommand=scrollbar.set,)
    # listbox.grid(row=1, column=0, sticky=tk.W, pady=0)
    # listbox.configure(font=("Helvetica", 12), foreground="black", background="white",
    #                selectbackground="lightblue", selectforeground="navy")
    # 配置滚动条与Listbox的交互
    scrollbar.config(command=tree.yview)

    # 创建弹出菜单
    menu = tk.Menu(tree, tearoff=0)
    menu.add_command(label="删除", command=lambda: delete_node(tree))
    # 绑定右键点击事件
    tree.bind("<Button-3>", lambda event: popup_menu(event, menu))

    # 初始化Tooltip实例
    treetooltip = gui_common.TreeToolTip(tree)
    # 绑定鼠标移动事件以显示/隐藏Tooltip
    tree.bind("<Motion>", lambda event: on_treeview_motion(event, treetooltip, tree))
    # 右边的文本框
    # 配置grid布局的列，让第1列自动扩展填充可用空间
    app.columnconfigure(1, weight=1)

    # 收帧（recv_frame）
    recv_frame = tk.Frame(app, padx=10, pady=10)
    recv_frame.grid(row=0, column=1, sticky=tk.NSEW)

    # 确保recv_title_label显示在recv_text上方
    recv_title_label = tk.Label(
        recv_frame,
        text="Recv Context",
        font=("Arial", 14, "bold"),
        fg="blue",
        anchor=tk.W,
    )
    recv_title_label.pack(
        side=tk.TOP, pady=(0, 5), fill=tk.BOTH
    )  # 保持此处不变，标题横跨宽度

    # 内部Frame用于放置滚动条和Text
    inner_frame = tk.Frame(recv_frame)
    inner_frame.pack(
        side=tk.BOTTOM, fill=tk.BOTH, expand=True
    )  # 修改为side=tk.TOP以使内部Frame位于顶部

    # 创建滚动条，并将其放置在内部Frame
    recv_scrollbar = tk.Scrollbar(inner_frame)
    recv_scrollbar.pack(side=tk.RIGHT, fill=tk.Y)

    # 将滚动条与recv_text关联
    recv_text = tk.Text(
        inner_frame, wrap=tk.WORD, yscrollcommand=recv_scrollbar.set, spacing1=5
    )
    recv_text.pack(side=tk.LEFT, fill=tk.BOTH, expand=True)

    # 配置滚动条的行为
    recv_scrollbar.config(command=recv_text.yview)

    # 发送帧（send_frame）
    send_frame = tk.Frame(app, padx=10, pady=10)
    send_frame.grid(row=1, column=1, sticky=tk.NSEW)
    send_title_label = tk.Label(
        send_frame,
        text="Send Context",
        font=("Arial", 14, "bold"),
        fg="blue",
        anchor=tk.W,
    )
    send_title_label.pack(side=tk.TOP, pady=(0, 5), fill=tk.BOTH)
    send_text = tk.Text(send_frame, wrap=tk.WORD, spacing1=5)
    send_text.pack(fill=tk.BOTH, expand=True)

    # 确保第二列自适应并充满可用空间
    app.grid_columnconfigure(1, weight=1)

    # 分配行权重，使两个Frame各自占据一半高度
    app.grid_rowconfigure(0, weight=1)
    app.grid_rowconfigure(1, weight=1)

    # 创建一个Frame来包裹按钮，便于控制它们的紧凑排列
    frame_btn = tk.Frame(app, padx=0, pady=0)
    frame_btn.grid(row=1, column=0, pady=(0, 0), sticky=tk.N)

    # 初始化 Combobox
    init_opition = [
        "127.0.0.1:9000",
        "8.140.193.207:9000",
    ]
    combobox = ttk.Combobox(frame_btn, values=init_opition)
    combobox.set("127.0.0.1:9000")
    combobox.grid(row=0, column=0, padx=10, pady=10)
    # 绑定文本变化的事件，监听用户输入
    combobox.bind(
        "<FocusOut>",
        lambda event: on_combobox_focusout(
            event=event, combobox=combobox, options=init_opition
        ),
    )

    # 创建一个按钮
    btn = tk.Button(
        frame_btn,
        text="创建连接",
        command=lambda: create_tcp_connection(tree, combobox, recv_text),
    )
    btn.grid(row=0, column=1, padx=0, pady=0)

    # 创建一个按钮
    btn_send = tk.Button(
        frame_btn,
        text="发送数据",
        command=lambda: send_tcp_data(tree, send_text, recv_text),
    )
    btn_send.grid(row=0, column=2, padx=2, pady=0)

    # 创建一个按钮
    btn_send = tk.Button(
        frame_btn, text="关闭连接", command=lambda: close_tcp_conn(tree)
    )
    btn_send.grid(row=0, column=3, padx=2, pady=0)

    gui_common.center_window(app, 1000, 400)
    app.protocol("WM_DELETE_WINDOW", lambda: on_closing(app))
    app.mainloop()  # 启动事件循环，等待用户交互
